# coding: utf-8

import json
import time

from ddutils.utils import str2int
from jobs.models import TaskLog, TaskConf, Task, TaskHostLog

from .. import constants as task_status


def get_model_instance(model, pk):
    try:
        instance = model.objects.get(id=pk)
    except:
        instance = None
    return instance


def string2list(data, split_flag=','):
    if isinstance(data, list):
        return data
    elif isinstance(data, tuple):
        return list(data)
    elif isinstance(data, basestring):
        try:
            split_list = data.split(split_flag)
        except:
            split_list = []
        return split_list
    return []


class BaseScheduler(object):
    '''
    根据主机数量将任务拆分, 如果主机数量过多,则将主机拆分成多个子任务去执行
    wait任务的执行状态,负责回收任务的执行状态和结果
    '''
    STATUS = ('QUEUE', 'PENDING', 'STARTED', 'SUCCESS', 'FAILURE', 'RETRY', 'REVOKED')

    def __init__(self, log_id, task_id, celery_id):
        self.task_log = get_model_instance(TaskLog, log_id)
        self.task = get_model_instance(Task, task_id)

        self.celery_id = celery_id

        self._hosts = []
        self._task_params = {}
        self._children_tasks = []

        self.listen_event = None
        self.listen_thread = None

    def is_validate(self):
        if self.task and self.task_log and self.hosts:
            return True
        return False

    @property
    def task_params(self):
        if not self._task_params:
            self._task_params = json.loads(self.task_log.params)
        return self._task_params or {}

    @property
    def hosts(self):
        if self._hosts: return self._hosts
        hosts = self.task_params.get('hosts', [])
        if isinstance(hosts, basestring):
            self._hosts = string2list(hosts)
        return self._hosts

    @property
    def timeout(self):
        return str2int(self.task_params.get('timeout', 600))

    def is_terminate(self):
        pass

    def finished(self):
        '''
        任务结束,保存任务状态到数据库,设置任务结果
        :return:
        '''
        self.listen_event.set()
        self.listen_thread.join()

    def load_from_db(self):
        '''
        加载所有的执行任务的主机从数据库，如果主机任务已经执行了,则把主机的状态置为被终止
        返回在队列中未被运行的任务
        :return:
        '''
        task_hosts = []
        for host in TaskHostLog.objects.filter(task_log_id=self.task_log.id).all():
            # 如果任务已经开始执行,但是未完成,则把任务的状态置为失败
            if host.status != task_status.QUEUE:
                host.status = task_status.FAILURE
                host.save()
            task_hosts.append(host)
        if len(task_hosts) == len(self.hosts):
            return task_hosts
        for host in self.hosts:
            host_task = self.host_task(host)
            if host_task not in task_hosts:
                task_hosts.append(host_task)
        return task_hosts

    def scheduler_hosts(self):
        return [task.host for task in self.load_from_db() if task.status == task_status.QUEUE]

    def host_task(self, host):
        try:
            task_host = TaskHostLog.objects.filter(task_log_id=self.task_log.id).filter(host=host).first()
        except TaskHostLog.DoesNotExist:
            task_host = None
        if not task_host:
            task_host = TaskHostLog(task_log_id=self.task_log.id, host=host,status=task_status.QUEUE,
                                    result_queue_id=self.celery_id)
            task_host.save()
        elif task_host.status != task_status.QUEUE:
            # 如果任务已经开始,则把这台主机的任务置为被终止
            task_host.status = task_status.FAILURE
            task_host.save()
        return task_host

    def retry(self):
        pass

    def save_task_result(self):
        status = []
        for task in TaskHostLog.objects.filter(task_log_id=self.task_log.id).all():
            # if task status not set  set failure
            if task.status not in task_status.FINISHED_STATUS:
                task.status = task_status.FAILURE
                task.save()
            status.append(task.status)

        if set(status).difference(set(task_status.SUCCESS_STATUS)):
            self.task_status(task_status.FAILURE)
        else:
            self.task_status(task_status.SUCCESS)

    def init(self):
        # set task to STARED
        self.task_status(task_status.STARTED)
        # load scheduler hosts from db
        scheduler_hosts = self.scheduler_hosts()
        # use define strategy
        host_groups = self.strategy(scheduler_hosts)
        # send task
        self.task_send(host_groups)
        # wait task
        self.wait()
        # check message queue
        self.recive_messages()
        # save task status
        self.save_task_result()
        # finished
        self.finished()

    def recive_messages(self):
        pass

    def wait(self):
        status = self.task_log.status
        start_time = time.time()
        while status not in task_status.FINISHED_STATUS:
            # check finished
            if self.children_tasks_finished:
                break
            now_time = time.time()
            # timeout
            if (now_time - start_time) > self.timeout:
                self.task_status(task_status.FAILURE)
                self.task.output = 'Terminate Task {}'.format(self.celery_id)
                self.task.save()
                break
            # reload task from db update task status
            task_log = get_model_instance(TaskLog, self.task_log.id)
            if task_log.status in task_status.FINISHED_STATUS:
                break
            self.listen_event.wait(1)

    @property
    def children_tasks_finished(self):
        status = set([self.children_task_status(task_id) for task_id in self._children_tasks])
        if status.difference(set(task_status.FINISHED_STATUS)):
            return False
        return True

    def children_task_status(self, task_id):
        pass

    def message_listen(self):
        while not self.listen_event.set():
            # save task result
            # if task.task_work is finished set task status
            pass
            self.listen_event.wait(5)

    @staticmethod
    def strategy(hosts):
        if isinstance(hosts, list):
            return hosts
        return [hosts]

    def task_send(self, host_groups):
        for group in host_groups:
            # send to worker
            pass
            task_id = 1
            self._children_tasks.append(task_id)

    def task_status(self, status):
        if status in self.STATUS:
            self.task_log.status = status
            self.task_log.save()
        return self.task_log.status
